using TMPro;
using UnityEngine;

/// <summary>
/// Paddle 类代表游戏中的一个“挡板”或“球拍”，玩家或 AI 可以控制它来击打球
/// </summary>
public class Paddle : MonoBehaviour
{
    // 使用静态只读整型常量，存储 Shader 属性的 ID，提高性能和代码可读性
    static readonly int
        emissionColorId = Shader.PropertyToID("_EmissionColor"),    // Shader 中“自发光颜色”属性的 ID
        faceColorId = Shader.PropertyToID("_FaceColor"),            // Shader 中“表面颜色”属性的 ID
        timeOfLastHitId = Shader.PropertyToID("_TimeOfLastHit");    // Shader 中记录“上次击中时间”的自定义属性 ID

    [SerializeField]
    TextMeshPro scoreText; // UI 文本组件，用于显示当前得分

    [SerializeField]
    MeshRenderer goalRenderer; // 球门区域对应的 Mesh 渲染器，用于显示得分特效

    [SerializeField, ColorUsage(true, true)]
    Color goalColor = Color.white; // 球门区域的颜色，支持 HDR 和透明度

    [SerializeField, Min(0f)]
    float
        minExtents = 4f,  // 挡板缩放的最小范围
        maxExtents = 4f,  // 挡板缩放的最大范围
        speed = 10f,      // 挡板移动的速度
        maxTargetingBias = 0.75f;  // AI 目标偏移的最大偏移量，用于让 AI 不总是精确瞄准

    [SerializeField]
    bool isAI; // 标记当前挡板是否由 AI 控制

    int score; // 当前得分

    float extents, // 当前挡板的缩放范围（extents）
          targetingBias;  // 当前 AI 的目标偏移量，用于调整 AI 行为策略

    Material goalMaterial, // 得分区域（goal）的材质，用于动态修改颜色或特效
            paddleMaterial,  // 挡板自身的材质，用于击中时高亮等反馈
            scoreMaterial;   // 得分文本的材质，用于根据得分改变颜色
    
    void Awake ()
    {
        // 获取得分区域（goal）的 MeshRenderer 上的材质，并设置其自发光颜色
        goalMaterial = goalRenderer.material;
        goalMaterial.SetColor(emissionColorId, goalColor);

        // 获取当前挡板对象的 MeshRenderer 组件上的材质
        paddleMaterial = GetComponent<MeshRenderer>().material;

        // 获取得分文本（TextMeshPro）使用的字体材质
        scoreMaterial = scoreText.fontMaterial;

        // 初始化得分为 0
        SetScore(0);
    }

    // 开始一场新游戏时调用，重置得分并随机化 AI 的目标偏移
    public void StartNewGame ()
    {
        SetScore(0);                      // 重置分数为 0
        ChangeTargetingBias();            // 随机设置一次 AI 的目标偏差
    }

    /// <summary>
    /// 检测球是否与挡板发生碰撞，返回是否成功击中，并输出击中位置比例
    /// </summary>
    public bool HitBall (float ballX, float ballExtents, out float hitFactor)
    {
        // 每次尝试击球时，都随机调整一下 AI 的目标偏移，增加不可预测性
        ChangeTargetingBias();

        // 计算击中因子：球的位置与挡板中心的位置差，除以两者“范围之和”
        // 该值用于确定球击中挡板的相对位置（-1 左侧，0 中心，1 右侧）
        hitFactor =
            (ballX - transform.localPosition.x) /
            (extents + ballExtents);

        // 判断击中是否有效：击中因子必须在 [-1, 1] 范围内，表示球确实打在了挡板范围内
        bool success = -1f <= hitFactor && hitFactor <= 1f;

        if (success)
        {
            // 如果成功击中，记录当前时间为“上次击中时间”，可用于视觉反馈（如高亮）
            paddleMaterial.SetFloat(timeOfLastHitId, Time.time);
        }

        return success; // 返回是否击中
    }

    /// <summary>
    /// 当球越过挡板进入得分区域时调用，增加得分并判断是否获胜
    /// </summary>
    public bool ScorePoint (int pointsToWin)
    {
        // 记录得分时刻，可用于触发视觉特效
        goalMaterial.SetFloat(timeOfLastHitId, Time.time);

        // 增加得分，并传入胜利所需的总分，可能用于更新 UI 或材质颜色
        SetScore(score + 1, pointsToWin);

        // 判断当前得分是否已经达到或超过胜利条件
        return score >= pointsToWin;
    }

    /// <summary>
    /// 移动挡板，根据目标位置和竞技场边界限制其范围
    /// </summary>
    public void Move (float target, float arenaExtents)
    {
        // 获取当前挡板的本地位置
        Vector3 p = transform.localPosition;

        // 根据当前是否是 AI，选择不同的移动策略
        p.x = isAI ? AdjustByAI(p.x, target) : AdjustByPlayer(p.x);

        // 计算挡板可移动的边界限制：竞技场边界减去挡板自身范围
        float limit = arenaExtents - extents;

        // 将挡板的 X 坐标限制在 [-limit, limit] 范围内，防止跑出边界
        p.x = Mathf.Clamp(p.x, -limit, limit);

        // 更新挡板的实际位置
        transform.localPosition = p;
    }

    /// <summary>
    /// AI 控制逻辑：根据当前挡板位置与目标位置计算下一步移动
    /// </summary>
    float AdjustByAI (float x, float target)
    {
        // 给目标位置添加一定的随机偏移，让 AI 不是每次都精准对准球，增加挑战性
        target += targetingBias * extents;

        if (x < target)
        {
            // 如果当前位置小于目标，向右移动（增加 x），但不超过目标位置
            return Mathf.Min(x + speed * Time.deltaTime, target);
        }
        
        // 否则向左移动（减小 x），但不小于目标位置
        return Mathf.Max(x - speed * Time.deltaTime, target);
    }

    /// <summary>
    /// 玩家控制逻辑：根据键盘输入（左右箭头键）控制挡板移动
    /// </summary>
    float AdjustByPlayer (float x)
    {
        bool goRight = Input.GetKey(KeyCode.RightArrow);    // 是否按下右箭头
        bool goLeft = Input.GetKey(KeyCode.LeftArrow);      // 是否按下左箭头

        if (goRight && !goLeft)
        {
            // 只按右键：向右移动
            return x + speed * Time.deltaTime;
        }
        else if (goLeft && !goRight)
        {
            // 只按左键：向左移动
            return x - speed * Time.deltaTime;
        }
        // 没有按键或同时按下两键：保持不动
        return x;
    }

    /// <summary>
    /// 设置当前得分，并更新 UI 与视觉效果
    /// </summary>
    void SetScore (int newScore, float pointsToWin = 1000f)
    {
        score = newScore;                           // 更新得分
        scoreText.SetText("{0}", newScore);         // 更新 TextMeshPro 显示的分数文本

        // 根据当前得分与胜利所需分数的比例，动态调整得分文字的颜色亮度
        scoreMaterial.SetColor(faceColorId, goalColor * (newScore / pointsToWin));

        // 根据得分进度，动态调整挡板的大小（缩放范围），得分越高挡板越小（或反之，取决于插值方向）
        SetExtents(Mathf.Lerp(maxExtents, minExtents, newScore / (pointsToWin - 1f)));
    }

    /// <summary>
    /// 随机生成一个新的 AI 目标偏移值，让 AI 行为具有一定随机性
    /// </summary>
    void ChangeTargetingBias () =>
        targetingBias = Random.Range(-maxTargetingBias, maxTargetingBias);

    /// <summary>
    /// 设置挡板当前的“范围”（extents）
    /// </summary>
    void SetExtents (float newExtents)
    {
        extents = newExtents;                         // 更新范围变量

        // 获取当前物体的缩放比例，并只修改 X 轴缩放，使其与 extents 成正比
        Vector3 s = transform.localScale;
        s.x = 2f * newExtents;                        // X 缩放设为 2 倍的 extents，
        transform.localScale = s;                     // 应用新的缩放值
    }
}